home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / DirectInput / ActionMapper / actionmapper.cpp next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  30.6 KB  |  891 lines

  1. //-----------------------------------------------------------------------------
  2. // File: ActionMapper.cpp
  3. //
  4. // Desc: This is a simple sample to demonstrate how to code using the DInput
  5. //       action mapper feature.
  6. //
  7. //-----------------------------------------------------------------------------
  8. #define STRICT
  9. #define DIRECTINPUT_VERSION 0x0800
  10.  
  11. #include <windows.h>
  12. #include <commctrl.h>
  13. #include <basetsd.h>
  14. #include <math.h>
  15. #include <stdio.h>
  16. #include <dxerr9.h>
  17. #include <tchar.h>
  18. #include <dinput.h>
  19. #include "DIUtil.h"
  20. #include "resource.h"
  21.  
  22.  
  23.  
  24.  
  25. //-----------------------------------------------------------------------------
  26. // Defines, and constants
  27. //-----------------------------------------------------------------------------
  28. // This GUID must be unique for every game, and the same for 
  29. // every instance of this app.  // {67131584-2938-4857-8A2E-D99DC2C82068}
  30. // The GUID allows DirectInput to remember input settings
  31. GUID g_guidApp = { 0x67131584, 0x2938, 0x4857, { 0x8a, 0x2e, 0xd9, 0x9d, 0xc2, 0xc8, 0x20, 0x68 } };
  32.  
  33.  
  34. // DirectInput action mapper reports events only when buttons/axis change
  35. // so we need to remember the present state of relevant axis/buttons for 
  36. // each DirectInput device.  The CInputDeviceManager will store a 
  37. // pointer for each device that points to this struct
  38. struct InputDeviceState
  39. {
  40.     FLOAT fAxisMoveUD;
  41.     BOOL  bButtonForwardThrust;
  42.     BOOL  bButtonReverseThrust;
  43.  
  44.     FLOAT fAxisRotateLR;
  45.     BOOL  bButtonRotateLeft;
  46.     BOOL  bButtonRotateRight;
  47.  
  48.     BOOL  bButtonFireWeapons;
  49.     BOOL  bButtonEnableShield;
  50. };
  51.  
  52.  
  53. // Struct to store the current input state
  54. struct UserInput
  55. {
  56.     FLOAT fAxisMoveUD;
  57.     FLOAT fAxisRotateLR;
  58.     BOOL  bButtonFireWeapons;
  59.     BOOL  bButtonEnableShield;
  60.  
  61.     BOOL  bDoConfigureInput;
  62.     BOOL  bDoQuitGame;
  63. };
  64.  
  65.  
  66. // Input semantics used by this app
  67. enum INPUT_SEMANTICS
  68. {
  69.     // Gameplay semantics
  70.     INPUT_ROTATE_AXIS_LR=1,  INPUT_MOVE_AXIS_UD,       
  71.     INPUT_FIREWEAPONS,       INPUT_ENABLESHIELD,    
  72.     INPUT_TURNLEFT,          INPUT_TURNRIGHT, 
  73.     INPUT_FORWARDTHRUST,     INPUT_REVERSETHRUST,
  74.     INPUT_DISPLAYGAMEMENU,   INPUT_QUITGAME,
  75. };
  76.  
  77. // Actions used by this app
  78. DIACTION g_rgGameAction[] =
  79. {
  80.     // Be sure to delete user map files 
  81.     // (C:\Program Files\Common Files\DirectX\DirectInput\User Maps\*.ini)
  82.     // after changing this, otherwise settings won't reset and will be read 
  83.     // from the out of date ini files 
  84.  
  85.     // Device input (joystick, etc.) that is pre-defined by DInput, according
  86.     // to genre type. The genre for this app is space simulators.
  87.     { INPUT_ROTATE_AXIS_LR,  DIAXIS_SPACESIM_LATERAL,      0, TEXT("Rotate left/right"), },
  88.     { INPUT_MOVE_AXIS_UD,    DIAXIS_SPACESIM_MOVE,         0, TEXT("Move"), },
  89.     { INPUT_FIREWEAPONS,     DIBUTTON_SPACESIM_FIRE,       0, TEXT("Fire weapons"), },
  90.     { INPUT_ENABLESHIELD,    DIBUTTON_SPACESIM_GEAR,       0, TEXT("Enable shield"), },
  91.     { INPUT_DISPLAYGAMEMENU, DIBUTTON_SPACESIM_DISPLAY,    0, TEXT("Configure"), },
  92.  
  93.     // Keyboard input mappings
  94.     { INPUT_TURNLEFT,        DIKEYBOARD_LEFT,    0, TEXT("Turn left"), },
  95.     { INPUT_TURNRIGHT,       DIKEYBOARD_RIGHT,   0, TEXT("Turn right"), },
  96.     { INPUT_FORWARDTHRUST,   DIKEYBOARD_UP,      0, TEXT("Forward thrust"), },
  97.     { INPUT_REVERSETHRUST,   DIKEYBOARD_DOWN,    0, TEXT("Reverse thrust"), },
  98.     { INPUT_FIREWEAPONS,     DIKEYBOARD_F,       0, TEXT("Fire weapons"), },
  99.     { INPUT_ENABLESHIELD,    DIKEYBOARD_S,       0, TEXT("Enable shield"), },
  100.     { INPUT_DISPLAYGAMEMENU, DIKEYBOARD_D,       DIA_APPFIXED, TEXT("Configure"), },
  101.     { INPUT_QUITGAME,        DIKEYBOARD_ESCAPE,  DIA_APPFIXED, TEXT("Quit game"), },
  102. };
  103.  
  104. #define NUMBER_OF_GAMEACTIONS    (sizeof(g_rgGameAction)/sizeof(DIACTION))
  105.  
  106.  
  107.  
  108.  
  109. //-----------------------------------------------------------------------------
  110. // Function prototypes 
  111. //-----------------------------------------------------------------------------
  112. INT_PTR CALLBACK StaticMsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
  113.  
  114.  
  115. //-----------------------------------------------------------------------------
  116. // Name: DXUtil_Timer()
  117. // Desc: Performs timer opertations. Use the following commands:
  118. //          TIMER_RESET           - to reset the timer
  119. //          TIMER_START           - to start the timer
  120. //          TIMER_STOP            - to stop (or pause) the timer
  121. //          TIMER_ADVANCE         - to advance the timer by 0.1 seconds
  122. //          TIMER_GETABSOLUTETIME - to get the absolute system time
  123. //          TIMER_GETAPPTIME      - to get the current time
  124. //          TIMER_GETELAPSEDTIME  - to get the time that elapsed between 
  125. //                                  TIMER_GETELAPSEDTIME calls
  126. //-----------------------------------------------------------------------------
  127. enum TIMER_COMMAND { TIMER_RESET, TIMER_START, TIMER_STOP, TIMER_ADVANCE,
  128.                      TIMER_GETABSOLUTETIME, TIMER_GETAPPTIME, TIMER_GETELAPSEDTIME };
  129. FLOAT __stdcall DXUtil_Timer( TIMER_COMMAND command );
  130.  
  131.  
  132.  
  133. //-----------------------------------------------------------------------------
  134. // Name: class CMyApplication 
  135. // Desc: Application class.
  136. //-----------------------------------------------------------------------------
  137. class CMyApplication 
  138. {
  139.     HWND                  m_hWnd;                       // The main app window
  140.     FLOAT                 m_fTime;                      // Current time in seconds
  141.     FLOAT                 m_fElapsedTime;               // Time elapsed since last frame
  142.  
  143.     CInputDeviceManager*  m_pInputDeviceManager;        // DirectInput device manager
  144.     DIACTIONFORMAT        m_diafGame;                   // Action format for game play
  145.     UserInput             m_UserInput;                  // Struct for storing user input 
  146.  
  147.     FLOAT                 m_fWorldRotX;                 // World rotation state X-axis
  148.     FLOAT                 m_fWorldRotY;                 // World rotation state Y-axis
  149.  
  150. protected:
  151.     HRESULT OneTimeSceneInit();
  152.     HRESULT Render();
  153.     HRESULT FrameMove();
  154.     HRESULT FinalCleanup();
  155.  
  156.     HRESULT InitInput( HWND hWnd );
  157.     void    UpdateInput( UserInput* pUserInput );
  158.     void    CleanupDirectInput();
  159.  
  160. public:
  161.     HRESULT Create( HINSTANCE hInstance );
  162.     INT     Run();
  163.     INT_PTR MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
  164.  
  165.     CMyApplication();
  166.  
  167.     HRESULT InputAddDeviceCB( CInputDeviceManager::DeviceInfo* pDeviceInfo, const DIDEVICEINSTANCE* pdidi );
  168.     static HRESULT CALLBACK StaticInputAddDeviceCB( CInputDeviceManager::DeviceInfo* pDeviceInfo, const DIDEVICEINSTANCE* pdidi, LPVOID pParam );   
  169. };
  170.  
  171.  
  172.  
  173.  
  174. //-----------------------------------------------------------------------------
  175. // Global access to the app (needed for the global WndProc())
  176. //-----------------------------------------------------------------------------
  177. CMyApplication*    g_pApp  = NULL;
  178. HINSTANCE          g_hInst = NULL;
  179.  
  180.  
  181.  
  182.  
  183. //-----------------------------------------------------------------------------
  184. // Name: WinMain()
  185. // Desc: Application entry point
  186. //-----------------------------------------------------------------------------
  187. INT WINAPI WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow )
  188. {
  189.     CMyApplication app;
  190.  
  191.     g_hInst = hInstance;
  192.  
  193.     InitCommonControls();
  194.  
  195.     if( FAILED( app.Create( hInstance ) ) )
  196.         return 0;
  197.  
  198.     return app.Run();
  199. }
  200.  
  201.  
  202.  
  203.  
  204. //-----------------------------------------------------------------------------
  205. // Name: CMyApplication()
  206. // Desc: Constructor
  207. //-----------------------------------------------------------------------------
  208. CMyApplication::CMyApplication()
  209. {
  210.     g_pApp                      = this;
  211.     m_hWnd                      = NULL;
  212.     m_pInputDeviceManager       = NULL;
  213.  
  214.     ZeroMemory( &m_UserInput, sizeof(m_UserInput) );
  215.     m_fWorldRotX                = 0.0f;
  216.     m_fWorldRotY                = 0.0f;
  217. }
  218.  
  219.  
  220.  
  221.  
  222. //-----------------------------------------------------------------------------
  223. // Name: Create()
  224. // Desc: Creates the window
  225. //-----------------------------------------------------------------------------
  226. HRESULT CMyApplication::Create( HINSTANCE hInstance )
  227. {
  228.     // Display the main dialog box.
  229.     CreateDialog( hInstance, MAKEINTRESOURCE(IDD_MAIN), 
  230.                   NULL, StaticMsgProc );
  231.     if( NULL == m_hWnd )
  232.         return E_FAIL;
  233.  
  234.     // Initialize the application timer
  235.     DXUtil_Timer( TIMER_START );
  236.  
  237.     return S_OK;
  238. }
  239.  
  240.  
  241.  
  242.  
  243. //-----------------------------------------------------------------------------
  244. // Name: Run()
  245. // Desc: Handles the message loop and calls FrameMove() and Render() when
  246. //       idle.
  247. //-----------------------------------------------------------------------------
  248. INT CMyApplication::Run()
  249. {
  250.     MSG msg;
  251.  
  252.     // Message loop to run the app
  253.     for( ; ; )
  254.     {
  255.         if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
  256.         {
  257.             // Skip WM_KEYDOWN so they aren't handled by the dialog
  258.             if( msg.message == WM_KEYDOWN )
  259.                 continue;
  260.  
  261.             if( !IsDialogMessage( m_hWnd, &msg ) )  
  262.             {
  263.                 TranslateMessage( &msg );
  264.                 DispatchMessage( &msg );
  265.             }
  266.  
  267.             if( msg.message == WM_QUIT )
  268.                 break;
  269.         }
  270.         else
  271.         {
  272.             // Update the time variables
  273.             m_fTime        = DXUtil_Timer( TIMER_GETAPPTIME );
  274.             m_fElapsedTime = DXUtil_Timer( TIMER_GETELAPSEDTIME );
  275.  
  276.             // This app uses idle time processing for the game loop
  277.             if( FAILED( FrameMove() ) )
  278.                 break;
  279.  
  280.             if( FAILED( Render() ) ) 
  281.                 break;
  282.             
  283.             Sleep( 20 );
  284.         }
  285.     }
  286.  
  287.     DestroyWindow( m_hWnd );
  288.     FinalCleanup();
  289.  
  290.     return (INT)msg.wParam;
  291. }
  292.  
  293.  
  294.  
  295.  
  296. //-----------------------------------------------------------------------------
  297. // Name: OneTimeSceneInit()
  298. // Desc: Called during initial app startup, this function performs all the
  299. //       permanent initialization.
  300. //-----------------------------------------------------------------------------
  301. HRESULT CMyApplication::OneTimeSceneInit()
  302. {
  303.     // Initialize DirectInput
  304.     InitInput( m_hWnd );
  305.  
  306.     return S_OK;
  307. }
  308.  
  309.  
  310.  
  311.  
  312. //-----------------------------------------------------------------------------
  313. // Name: StaticInputAddDeviceCB()
  314. // Desc: Static callback helper to call into CMyApplication class
  315. //-----------------------------------------------------------------------------
  316. HRESULT CALLBACK CMyApplication::StaticInputAddDeviceCB( 
  317.                                          CInputDeviceManager::DeviceInfo* pDeviceInfo, 
  318.                                          const DIDEVICEINSTANCE* pdidi, 
  319.                                          LPVOID pParam )
  320. {
  321.     CMyApplication* pApp = (CMyApplication*) pParam;
  322.     return pApp->InputAddDeviceCB( pDeviceInfo, pdidi );
  323. }
  324.  
  325.  
  326.  
  327.  
  328. //-----------------------------------------------------------------------------
  329. // Name: InputAddDeviceCB()
  330. // Desc: Called from CInputDeviceManager whenever a device is added. 
  331. //       Set the dead zone, and creates a new InputDeviceState for each device
  332. //-----------------------------------------------------------------------------
  333. HRESULT CMyApplication::InputAddDeviceCB( CInputDeviceManager::DeviceInfo* pDeviceInfo, 
  334.                                                    const DIDEVICEINSTANCE* pdidi )
  335. {
  336.     // Setup the deadzone 
  337.     DIPROPDWORD dipdw;
  338.     dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
  339.     dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  340.     dipdw.diph.dwObj        = 0;
  341.     dipdw.diph.dwHow        = DIPH_DEVICE;
  342.     dipdw.dwData            = 500;
  343.     pDeviceInfo->pdidDevice->SetProperty( DIPROP_DEADZONE, &dipdw.diph );
  344.  
  345.     // Create a new InputDeviceState for each device so the 
  346.     // app can record its state 
  347.     InputDeviceState* pNewInputDeviceState = new InputDeviceState;
  348.     if (!pNewInputDeviceState)
  349.         return E_FAIL;
  350.     
  351.     ZeroMemory( pNewInputDeviceState, sizeof(InputDeviceState) );
  352.     pDeviceInfo->pParam = (LPVOID) pNewInputDeviceState;
  353.     return S_OK;
  354. }
  355.  
  356.  
  357.  
  358.  
  359. //-----------------------------------------------------------------------------
  360. // Name: InitInput()
  361. // Desc: Initialize DirectInput objects
  362. //-----------------------------------------------------------------------------
  363. HRESULT CMyApplication::InitInput( HWND hWnd )
  364. {
  365.     HRESULT hr;
  366.  
  367.     // Setup action format for the actual gameplay
  368.     ZeroMemory( &m_diafGame, sizeof(DIACTIONFORMAT) );
  369.     m_diafGame.dwSize          = sizeof(DIACTIONFORMAT);
  370.     m_diafGame.dwActionSize    = sizeof(DIACTION);
  371.     m_diafGame.dwDataSize      = NUMBER_OF_GAMEACTIONS * sizeof(DWORD);
  372.     m_diafGame.guidActionMap   = g_guidApp;
  373.     m_diafGame.dwGenre         = DIVIRTUAL_SPACESIM; 
  374.     m_diafGame.dwNumActions    = NUMBER_OF_GAMEACTIONS;
  375.     m_diafGame.rgoAction       = g_rgGameAction;
  376.     m_diafGame.lAxisMin        = -100;
  377.     m_diafGame.lAxisMax        = 100;
  378.     m_diafGame.dwBufferSize    = 16;
  379.     _tcscpy( m_diafGame.tszActionMap, _T("ActionMapper Sample") );
  380.  
  381.     // Create a new input device manager
  382.     m_pInputDeviceManager = new CInputDeviceManager();
  383.     if( NULL == m_pInputDeviceManager )
  384.         return DXTRACE_ERR_MSGBOX( TEXT("InitInput"), E_OUTOFMEMORY );
  385.  
  386.     if( FAILED( hr = m_pInputDeviceManager->Create( hWnd, NULL, m_diafGame, 
  387.                                                     StaticInputAddDeviceCB, this ) ) )
  388.         return DXTRACE_ERR( TEXT("m_pInputDeviceManager->Create"), hr );
  389.  
  390.     return S_OK;
  391. }
  392.  
  393.  
  394.  
  395.  
  396. //-----------------------------------------------------------------------------
  397. // Name: FrameMove()
  398. // Desc: Called once per frame, the call is the entry point for animating
  399. //       the scene.
  400. //-----------------------------------------------------------------------------
  401. HRESULT CMyApplication::FrameMove()
  402. {
  403.     HRESULT hr = S_OK;
  404.  
  405.     // Update user input state
  406.     UpdateInput( &m_UserInput );
  407.  
  408.     // Respond to input
  409.     if( m_UserInput.bDoConfigureInput )
  410.     {
  411.         // One-shot per keypress
  412.         m_UserInput.bDoConfigureInput = FALSE;
  413.  
  414.         // Get access to the list of semantically-mapped input devices
  415.         // to delete all InputDeviceState structs before calling ConfigureDevices()
  416.         CInputDeviceManager::DeviceInfo* pDeviceInfos;
  417.         DWORD dwNumDevices;
  418.         m_pInputDeviceManager->GetDevices( &pDeviceInfos, &dwNumDevices );
  419.  
  420.         for( DWORD i=0; i<dwNumDevices; i++ )
  421.         {
  422.             InputDeviceState* pInputDeviceState = (InputDeviceState*) pDeviceInfos[i].pParam;
  423.             SAFE_DELETE( pInputDeviceState );
  424.             pDeviceInfos[i].pParam = NULL;
  425.         }
  426.  
  427.         // Configure the devices (with edit capability)
  428.         hr = m_pInputDeviceManager->ConfigureDevices( m_hWnd, NULL, NULL, DICD_EDIT, NULL );
  429.         if( FAILED(hr) )
  430.             return DXTRACE_ERR_MSGBOX( TEXT("ConfigureDevices"), hr );
  431.     }
  432.  
  433.     // Update the world state according to user input
  434.     if( m_UserInput.fAxisRotateLR )
  435.         m_fWorldRotY += m_fElapsedTime * m_UserInput.fAxisRotateLR;
  436.  
  437.     if( m_UserInput.fAxisMoveUD )
  438.         m_fWorldRotX += m_fElapsedTime * m_UserInput.fAxisMoveUD;
  439.  
  440.     return S_OK;
  441. }
  442.  
  443.  
  444.  
  445.  
  446. //-----------------------------------------------------------------------------
  447. // Name: UpdateInput()
  448. // Desc: Update the user input.  Called once per frame 
  449. //-----------------------------------------------------------------------------
  450. void CMyApplication::UpdateInput( UserInput* pUserInput )
  451. {
  452.     if( NULL == m_pInputDeviceManager )
  453.         return;
  454.  
  455.     // Get access to the list of semantically-mapped input devices
  456.     CInputDeviceManager::DeviceInfo* pDeviceInfos;
  457.     DWORD dwNumDevices;
  458.     m_pInputDeviceManager->GetDevices( &pDeviceInfos, &dwNumDevices );
  459.  
  460.     // Loop through all devices and check game input
  461.     for( DWORD i=0; i<dwNumDevices; i++ )
  462.     {
  463.         DIDEVICEOBJECTDATA rgdod[10];
  464.         DWORD   dwItems = 10;
  465.         HRESULT hr;
  466.         LPDIRECTINPUTDEVICE8 pdidDevice = pDeviceInfos[i].pdidDevice;
  467.         InputDeviceState* pInputDeviceState = (InputDeviceState*) pDeviceInfos[i].pParam;
  468.  
  469.         pdidDevice->Acquire();
  470.         pdidDevice->Poll();
  471.         
  472.         hr = pdidDevice->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
  473.                                         rgdod, &dwItems, 0 );
  474.         if( FAILED(hr) )
  475.             continue;
  476.  
  477.         // Get the sematics codes for the game menu
  478.         for( DWORD j=0; j<dwItems; j++ )
  479.         {
  480.             BOOL  bButtonState = (rgdod[j].dwData==0x80) ? TRUE : FALSE;
  481.             FLOAT fAxisState   = (FLOAT)((int)rgdod[j].dwData)/100.0f;
  482.  
  483.             switch( rgdod[j].uAppData )
  484.             {
  485.                 // Handle relative axis data
  486.                 case INPUT_ROTATE_AXIS_LR: 
  487.                     pInputDeviceState->fAxisRotateLR = fAxisState;
  488.                     break;
  489.                 case INPUT_MOVE_AXIS_UD:
  490.                     pInputDeviceState->fAxisMoveUD = -fAxisState;
  491.                     break;
  492.  
  493.                 // Handle buttons separately so the button state data
  494.                 // doesn't overwrite the axis state data, and handle
  495.                 // each button separately so they don't overwrite each other
  496.                 case INPUT_TURNLEFT:        pInputDeviceState->bButtonRotateLeft    = bButtonState; break;
  497.                 case INPUT_TURNRIGHT:       pInputDeviceState->bButtonRotateRight   = bButtonState; break;
  498.                 case INPUT_FORWARDTHRUST:   pInputDeviceState->bButtonForwardThrust = bButtonState; break;
  499.                 case INPUT_REVERSETHRUST:   pInputDeviceState->bButtonReverseThrust = bButtonState; break;
  500.                 case INPUT_FIREWEAPONS:     pInputDeviceState->bButtonFireWeapons   = bButtonState; break;
  501.                 case INPUT_ENABLESHIELD:    pInputDeviceState->bButtonEnableShield  = bButtonState; break;
  502.  
  503.                 // Handle one-shot buttons
  504.                 case INPUT_DISPLAYGAMEMENU: if( bButtonState ) pUserInput->bDoConfigureInput = TRUE; break;
  505.                 case INPUT_QUITGAME:        if( bButtonState ) pUserInput->bDoQuitGame       = TRUE; break;
  506.             }
  507.         }
  508.     }
  509.  
  510.     // Process user input and store result into pUserInput struct
  511.     pUserInput->fAxisRotateLR = 0.0f;
  512.     pUserInput->fAxisMoveUD   = 0.0f;
  513.     pUserInput->bButtonFireWeapons  = FALSE;
  514.     pUserInput->bButtonEnableShield = FALSE;
  515.  
  516.     // Concatinate the data from all the DirectInput devices
  517.     for( i=0; i<dwNumDevices; i++ )
  518.     {
  519.         InputDeviceState* pInputDeviceState = (InputDeviceState*) pDeviceInfos[i].pParam;
  520.  
  521.         // Use the axis data that is furthest from zero
  522.         if( fabs(pInputDeviceState->fAxisRotateLR) > fabs(pUserInput->fAxisRotateLR) )
  523.             pUserInput->fAxisRotateLR = pInputDeviceState->fAxisRotateLR;
  524.  
  525.         if( fabs(pInputDeviceState->fAxisMoveUD) > fabs(pUserInput->fAxisMoveUD) )
  526.             pUserInput->fAxisMoveUD = pInputDeviceState->fAxisMoveUD;
  527.  
  528.         // Process the button data 
  529.         if( pInputDeviceState->bButtonRotateLeft )
  530.             pUserInput->fAxisRotateLR = -1.0f;
  531.         else if( pInputDeviceState->bButtonRotateRight )
  532.             pUserInput->fAxisRotateLR = 1.0f;
  533.  
  534.         if( pInputDeviceState->bButtonForwardThrust )
  535.             pUserInput->fAxisMoveUD = 1.0f;
  536.         else if( pInputDeviceState->bButtonReverseThrust )
  537.             pUserInput->fAxisMoveUD = -1.0f;
  538.  
  539.         if( pInputDeviceState->bButtonFireWeapons )
  540.             pUserInput->bButtonFireWeapons = TRUE;
  541.         if( pInputDeviceState->bButtonEnableShield )
  542.             pUserInput->bButtonEnableShield = TRUE;
  543.     } 
  544. }
  545.  
  546.  
  547.  
  548.  
  549. //-----------------------------------------------------------------------------
  550. // Name: Render()
  551. // Desc: Called once per frame, the call is the entry point for rendering the 
  552. //       world.
  553. //-----------------------------------------------------------------------------
  554. HRESULT CMyApplication::Render()
  555. {
  556.     TCHAR szMsg[MAX_PATH];
  557.     TCHAR szMsgCurrent[MAX_PATH];
  558.  
  559.     _stprintf( szMsg, TEXT("%0.2f"), m_UserInput.fAxisMoveUD );
  560.     GetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE), szMsgCurrent, MAX_PATH );
  561.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  562.         SetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE), szMsg );
  563.  
  564.     _stprintf( szMsg, TEXT("%0.2f"), m_UserInput.fAxisRotateLR );
  565.     GetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE), szMsgCurrent, MAX_PATH );
  566.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  567.         SetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE), szMsg );
  568.  
  569.     if( !m_UserInput.bButtonEnableShield && !m_UserInput.bButtonFireWeapons )
  570.     {
  571.         _stprintf( szMsg, TEXT("None") );
  572.     }
  573.     else
  574.     {
  575.         _stprintf( szMsg, TEXT("%s%s"), m_UserInput.bButtonEnableShield ? TEXT("Shield ") : TEXT(""), 
  576.                                       m_UserInput.bButtonFireWeapons ? TEXT("Fire ") : TEXT("") );
  577.     }
  578.  
  579.     GetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE), szMsgCurrent, MAX_PATH );
  580.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  581.         SetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE), szMsg );
  582.  
  583.     _stprintf( szMsg, TEXT("%0.3f, %0.3f"), m_fWorldRotX, m_fWorldRotY );
  584.     GetWindowText( GetDlgItem(m_hWnd,IDC_WORLD_STATE), szMsgCurrent, MAX_PATH );
  585.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  586.         SetWindowText( GetDlgItem(m_hWnd,IDC_WORLD_STATE), szMsg );
  587.  
  588.     return S_OK;
  589. }
  590.  
  591.  
  592.  
  593.  
  594. //-----------------------------------------------------------------------------
  595. // Name: StaticMsgProc()
  596. // Desc: Static msg handler which passes messages to the application class.
  597. //-----------------------------------------------------------------------------
  598. INT_PTR CALLBACK StaticMsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  599. {
  600.     return g_pApp->MsgProc( hWnd, uMsg, wParam, lParam );
  601. }
  602.  
  603.  
  604.  
  605.  
  606. //-----------------------------------------------------------------------------
  607. // Name: MsgProc()
  608. // Desc: Callback for all Windows messages
  609. //-----------------------------------------------------------------------------
  610. INT_PTR CMyApplication::MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
  611. {
  612.     HRESULT hr;
  613.  
  614.     switch( msg )
  615.     {
  616.         case WM_INITDIALOG:
  617.             m_hWnd = hWnd;
  618.  
  619.             // Initialize the app's custom scene stuff
  620.             if( FAILED( hr = OneTimeSceneInit() ) )
  621.             {
  622.                 DXTRACE_ERR_MSGBOX( TEXT("OneTimeSceneInit"), hr );
  623.                 MessageBox( hWnd, TEXT("Error initializing DirectInput.  Sample will now exit."), 
  624.                                   TEXT("DirectInput Sample"), MB_OK | MB_ICONERROR );
  625.                 PostQuitMessage( IDCANCEL );
  626.                 return TRUE;
  627.             }
  628.             break;
  629.  
  630.         case WM_COMMAND:
  631.         {
  632.             switch( LOWORD(wParam) )
  633.             {
  634.                 case IDCANCEL:
  635.                     PostQuitMessage( IDCANCEL );
  636.                     break;
  637.  
  638.                 case IDM_CONFIGINPUT:
  639.                     m_UserInput.bDoConfigureInput = TRUE;
  640.                     break;
  641.             }
  642.             break;
  643.         }
  644.     }
  645.  
  646.     return FALSE;
  647. }
  648.  
  649.  
  650.  
  651.  
  652. //-----------------------------------------------------------------------------
  653. // Name: FinalCleanup()
  654. // Desc: Called before the app exits, this function gives the app the chance
  655. //       to cleanup after itself.
  656. //-----------------------------------------------------------------------------
  657. HRESULT CMyApplication::FinalCleanup()
  658. {
  659.     // Cleanup DirectInput
  660.     CleanupDirectInput();
  661.  
  662.     return S_OK;
  663. }
  664.  
  665.  
  666.  
  667.  
  668. //-----------------------------------------------------------------------------
  669. // Name: CleanupDirectInput()
  670. // Desc: Cleanup DirectInput 
  671. //-----------------------------------------------------------------------------
  672. VOID CMyApplication::CleanupDirectInput()
  673. {
  674.     if( NULL == m_pInputDeviceManager )
  675.         return;
  676.  
  677.     // Get access to the list of semantically-mapped input devices
  678.     // to delete all InputDeviceState structs
  679.     CInputDeviceManager::DeviceInfo* pDeviceInfos;
  680.     DWORD dwNumDevices;
  681.     m_pInputDeviceManager->GetDevices( &pDeviceInfos, &dwNumDevices );
  682.  
  683.     for( DWORD i=0; i<dwNumDevices; i++ )
  684.     {
  685.         InputDeviceState* pInputDeviceState = (InputDeviceState*) pDeviceInfos[i].pParam;
  686.         SAFE_DELETE( pInputDeviceState );
  687.         pDeviceInfos[i].pParam = NULL;
  688.     }
  689.  
  690.     // Cleanup DirectX input objects
  691.     SAFE_DELETE( m_pInputDeviceManager );
  692. }
  693.  
  694.  
  695.  
  696.  
  697.  
  698. //-----------------------------------------------------------------------------
  699. // Name: DXUtil_Timer()
  700. // Desc: Performs timer opertations. Use the following commands:
  701. //          TIMER_RESET           - to reset the timer
  702. //          TIMER_START           - to start the timer
  703. //          TIMER_STOP            - to stop (or pause) the timer
  704. //          TIMER_ADVANCE         - to advance the timer by 0.1 seconds
  705. //          TIMER_GETABSOLUTETIME - to get the absolute system time
  706. //          TIMER_GETAPPTIME      - to get the current time
  707. //          TIMER_GETELAPSEDTIME  - to get the time that elapsed between 
  708. //                                  TIMER_GETELAPSEDTIME calls
  709. //-----------------------------------------------------------------------------
  710. FLOAT __stdcall DXUtil_Timer( TIMER_COMMAND command )
  711. {
  712.     static BOOL     m_bTimerInitialized = FALSE;
  713.     static BOOL     m_bUsingQPF         = FALSE;
  714.     static BOOL     m_bTimerStopped     = TRUE;
  715.     static LONGLONG m_llQPFTicksPerSec  = 0;
  716.  
  717.     // Initialize the timer
  718.     if( FALSE == m_bTimerInitialized )
  719.     {
  720.         m_bTimerInitialized = TRUE;
  721.  
  722.         // Use QueryPerformanceFrequency() to get frequency of timer.  If QPF is
  723.         // not supported, we will timeGetTime() which returns milliseconds.
  724.         LARGE_INTEGER qwTicksPerSec;
  725.         m_bUsingQPF = QueryPerformanceFrequency( &qwTicksPerSec );
  726.         if( m_bUsingQPF )
  727.             m_llQPFTicksPerSec = qwTicksPerSec.QuadPart;
  728.     }
  729.  
  730.     if( m_bUsingQPF )
  731.     {
  732.         static LONGLONG m_llStopTime        = 0;
  733.         static LONGLONG m_llLastElapsedTime = 0;
  734.         static LONGLONG m_llBaseTime        = 0;
  735.         double fTime;
  736.         double fElapsedTime;
  737.         LARGE_INTEGER qwTime;
  738.         
  739.         // Get either the current time or the stop time, depending
  740.         // on whether we're stopped and what command was sent
  741.         if( m_llStopTime != 0 && command != TIMER_START && command != TIMER_GETABSOLUTETIME)
  742.             qwTime.QuadPart = m_llStopTime;
  743.         else
  744.             QueryPerformanceCounter( &qwTime );
  745.  
  746.         // Return the elapsed time
  747.         if( command == TIMER_GETELAPSEDTIME )
  748.         {
  749.             fElapsedTime = (double) ( qwTime.QuadPart - m_llLastElapsedTime ) / (double) m_llQPFTicksPerSec;
  750.             m_llLastElapsedTime = qwTime.QuadPart;
  751.             return (FLOAT) fElapsedTime;
  752.         }
  753.     
  754.         // Return the current time
  755.         if( command == TIMER_GETAPPTIME )
  756.         {
  757.             double fAppTime = (double) ( qwTime.QuadPart - m_llBaseTime ) / (double) m_llQPFTicksPerSec;
  758.             return (FLOAT) fAppTime;
  759.         }
  760.     
  761.         // Reset the timer
  762.         if( command == TIMER_RESET )
  763.         {
  764.             m_llBaseTime        = qwTime.QuadPart;
  765.             m_llLastElapsedTime = qwTime.QuadPart;
  766.             m_llStopTime        = 0;
  767.             m_bTimerStopped     = FALSE;
  768.             return 0.0f;
  769.         }
  770.     
  771.         // Start the timer
  772.         if( command == TIMER_START )
  773.         {
  774.             if( m_bTimerStopped )
  775.                 m_llBaseTime += qwTime.QuadPart - m_llStopTime;
  776.             m_llStopTime = 0;
  777.             m_llLastElapsedTime = qwTime.QuadPart;
  778.             m_bTimerStopped = FALSE;
  779.             return 0.0f;
  780.         }
  781.     
  782.         // Stop the timer
  783.         if( command == TIMER_STOP )
  784.         {
  785.             if( !m_bTimerStopped )
  786.             {
  787.                 m_llStopTime = qwTime.QuadPart;
  788.                 m_llLastElapsedTime = qwTime.QuadPart;
  789.                 m_bTimerStopped = TRUE;
  790.             }
  791.             return 0.0f;
  792.         }
  793.     
  794.         // Advance the timer by 1/10th second
  795.         if( command == TIMER_ADVANCE )
  796.         {
  797.             m_llStopTime += m_llQPFTicksPerSec/10;
  798.             return 0.0f;
  799.         }
  800.  
  801.         if( command == TIMER_GETABSOLUTETIME )
  802.         {
  803.             fTime = qwTime.QuadPart / (double) m_llQPFTicksPerSec;
  804.             return (FLOAT) fTime;
  805.         }
  806.  
  807.         return -1.0f; // Invalid command specified
  808.     }
  809.     else
  810.     {
  811.         // Get the time using timeGetTime()
  812.         static double m_fLastElapsedTime  = 0.0;
  813.         static double m_fBaseTime         = 0.0;
  814.         static double m_fStopTime         = 0.0;
  815.         double fTime;
  816.         double fElapsedTime;
  817.         
  818.         // Get either the current time or the stop time, depending
  819.         // on whether we're stopped and what command was sent
  820.         if( m_fStopTime != 0.0 && command != TIMER_START && command != TIMER_GETABSOLUTETIME)
  821.             fTime = m_fStopTime;
  822.         else
  823.             fTime = GetTickCount() * 0.001;
  824.     
  825.         // Return the elapsed time
  826.         if( command == TIMER_GETELAPSEDTIME )
  827.         {   
  828.             fElapsedTime = (double) (fTime - m_fLastElapsedTime);
  829.             m_fLastElapsedTime = fTime;
  830.             return (FLOAT) fElapsedTime;
  831.         }
  832.     
  833.         // Return the current time
  834.         if( command == TIMER_GETAPPTIME )
  835.         {
  836.             return (FLOAT) (fTime - m_fBaseTime);
  837.         }
  838.     
  839.         // Reset the timer
  840.         if( command == TIMER_RESET )
  841.         {
  842.             m_fBaseTime         = fTime;
  843.             m_fLastElapsedTime  = fTime;
  844.             m_fStopTime         = 0;
  845.             m_bTimerStopped     = FALSE;
  846.             return 0.0f;
  847.         }
  848.     
  849.         // Start the timer
  850.         if( command == TIMER_START )
  851.         {
  852.             if( m_bTimerStopped )
  853.                 m_fBaseTime += fTime - m_fStopTime;
  854.             m_fStopTime = 0.0f;
  855.             m_fLastElapsedTime  = fTime;
  856.             m_bTimerStopped = FALSE;
  857.             return 0.0f;
  858.         }
  859.     
  860.         // Stop the timer
  861.         if( command == TIMER_STOP )
  862.         {
  863.             if( !m_bTimerStopped )
  864.             {
  865.                 m_fStopTime = fTime;
  866.                 m_fLastElapsedTime  = fTime;
  867.                 m_bTimerStopped = TRUE;
  868.             }
  869.             return 0.0f;
  870.         }
  871.     
  872.         // Advance the timer by 1/10th second
  873.         if( command == TIMER_ADVANCE )
  874.         {
  875.             m_fStopTime += 0.1f;
  876.             return 0.0f;
  877.         }
  878.  
  879.         if( command == TIMER_GETABSOLUTETIME )
  880.         {
  881.             return (FLOAT) fTime;
  882.         }
  883.  
  884.         return -1.0f; // Invalid command specified
  885.     }
  886. }
  887.  
  888.  
  889.  
  890.  
  891.